package com.ymt.bpm.engine.listener;

import com.ymt.bpm.appboot.SpringContext;
import com.ymt.bpm.engine.listener.engine.exception.EngineException;
import com.ymt.bpm.engine.listener.engine.exception.ErrorCode;
import com.ymt.bpm.engine.handler.ActivityEventHandler;
import com.ymt.bpm.engine.handler.process.SequenceFlowEventHandler;
import com.ymt.bpm.service.platform.log.SysLogService;
import org.activiti.engine.delegate.event.ActivitiActivityEvent;
import org.activiti.engine.delegate.event.ActivitiEvent;
import org.activiti.engine.delegate.event.ActivitiEventListener;
import org.activiti.engine.delegate.event.ActivitiSequenceFlowTakenEvent;
import org.activiti.engine.runtime.Execution;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Created by Johnny on 2016/12/24.
 */
@Component
public class ActivityEventListener implements ActivitiEventListener {

    /**
     * Logger
     */
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private SequenceFlowEventHandler sequenceFlowEventHandler;

    @Override
    //@Transactional(EngineConst.TRANSACTION_MANAGER)
    public void onEvent(ActivitiEvent activitiEvent) {
        String executionId = activitiEvent.getExecutionId();
        if (logger.isDebugEnabled()) {
            logger.debug("onEvent============getType=" + activitiEvent.getType());
            logger.debug("onEvent============getExecutionId=" + activitiEvent.getExecutionId());
        }

        try {
            if (activitiEvent instanceof ActivitiActivityEvent) {
                onActivitiActivityEvent((ActivitiActivityEvent)activitiEvent);
            } else if (activitiEvent instanceof ActivitiSequenceFlowTakenEvent) {
                onActivitiSequenceFlowTakenEvent((ActivitiSequenceFlowTakenEvent)activitiEvent);
            } else {
                //do nothing
            }
        } catch (Exception e) {
            e.printStackTrace();
            EngineException e1 = new EngineException(
                    ErrorCode.ATVITIY_FAIL,
                    "Activity getting ActivityEventHandler failed on event:"+activitiEvent.getType()+", executionId="+activitiEvent.getExecutionId(),
                    e);
            try {
                SysLogService logService = SpringContext.getBean(SysLogService.class);
                Execution execution = activitiEvent.getEngineServices().getRuntimeService().createExecutionQuery().executionId(activitiEvent.getExecutionId()).singleResult();
                logService.expLog(execution.getTenantId(),
                        execution.getProcessInstanceId(),
                        execution.getActivityId(),
                        "",
                        "0",
                        null, null, e1);
            } catch (Exception e2) {
                e1.printStackTrace();
            }
            throw new RuntimeException(e1);
        }

    }

    @Override
    public boolean isFailOnException() {
        return true;
    }

    private void onActivitiActivityEvent(ActivitiActivityEvent aae) throws EngineException {
        if (logger.isDebugEnabled()) {
            logger.debug("onEvent============getType=" + aae.getType());
            logger.debug("onEvent============getExecutionId=" + aae.getExecutionId());
            logger.debug("onEvent============getActivityType=" + aae.getActivityType());
            logger.debug("onEvent============getActivityId=" + aae.getActivityId());
            logger.debug("onEvent============getActivityName=" + aae.getActivityName());
        }
        ActivityEventHandler activityEventHandler = getHandlerBean(aae);
        switch (aae.getType()) {
            case ACTIVITY_STARTED:
                activityEventHandler.onActivityStarted((ActivitiActivityEvent)aae);
                break;
            case ACTIVITY_COMPLETED:
                activityEventHandler.onActivityComplete((ActivitiActivityEvent)aae);
                break;
            case ACTIVITY_SIGNALED:
                //do nothing
            //case more:
            default:
                //do nothing
        }
    }

    private void onActivitiSequenceFlowTakenEvent(ActivitiSequenceFlowTakenEvent asfte) {
        if (logger.isDebugEnabled()) {
            logger.debug("onEvent============getSourceActivityBehaviorClass=" + asfte.getSourceActivityBehaviorClass());
            logger.debug("onEvent============getSourceActivityType=" + asfte.getSourceActivityType());
            logger.debug("onEvent============getSourceActivityId=" + asfte.getSourceActivityId());
            logger.debug("onEvent============getSourceActivityName=" + asfte.getSourceActivityName());
            logger.debug("onEvent============getSourceActivityBehaviorClass=" + asfte.getSourceActivityBehaviorClass());
            logger.debug("onEvent============getTargetActivityType=" + asfte.getTargetActivityType());
            logger.debug("onEvent============getTargetActivityId=" + asfte.getTargetActivityId());
            logger.debug("onEvent============getTargetActivityName=" + asfte.getTargetActivityName());
            logger.debug("onEvent============getTargetActivityBehaviorClass=" + asfte.getTargetActivityBehaviorClass());
        }
        switch (asfte.getType()) {
            case SEQUENCEFLOW_TAKEN:
                sequenceFlowEventHandler.onTake(asfte);
                break;
            default:
                //do nothing
        }
    }

    /**
     * get bean of event handlers
     * @param activitiEvent
     * @return
     */
    private ActivityEventHandler getHandlerBean(ActivitiActivityEvent activitiEvent) throws EngineException {
        ActivityEventHandler activityEventHandler = null;
        /*switch (activitiEvent.getActivityType()) {
            case "UserTask":
                handler = SpringContext.getBean(UserTaskEventHandler.class);
                break;
            case "ServiceTask":
                handler = SpringContext.getBean(ServiceTaskEventHandler.class);
                break;
            case "ExclusiveGateway":
                handler = SpringContext.getBean(ExclusiveGatewayHandler.class);
                break;
            case "InclusiveGateway":
                handler = SpringContext.getBean(InclusiveGatewayHandler.class);
                break;
            case "ParallelGateway":
                handler = SpringContext.getBean(ParallelGatewayHandler.class);
                break;
            case "StartEvent":
                handler = SpringContext.getBean(StartEventHandler.class);
                break;
            case "EndEvent":
                handler = SpringContext.getBean(EndEventHandler.class);
                break;
            case "IntermediateThrowEvent":
                handler = SpringContext.getBean(IntermediateThrowEventHandler.class);
                break;
            case "IntermediateCatchEvent":
                handler = SpringContext.getBean(IntermediateCatchEventHandler.class);
                break;
            case "BoundaryThrowEvent":
                handler = SpringContext.getBean(BoundaryThrowEventHandler.class);
                break;
            case "BoundaryCatchEvent":
                handler = SpringContext.getBean(BoundaryCatchEventHandler.class);
                break;
        }*/
        try {
            activityEventHandler = SpringContext.getBean(activitiEvent.getActivityType()+"Handler");
        } catch (Exception e) {
            e.printStackTrace();
            throw new EngineException(
                    ErrorCode.ATVITIY_FAIL,
                    "Activity getting ActivityEventHandler failed on event:"+activitiEvent.getType()+", executionId="+activitiEvent.getExecutionId(),
                    e);
        }
        if (activityEventHandler==null) {
            throw new EngineException(
                    ErrorCode.ATVITIY_FAIL,
                    "Activity getting ActivityEventHandler failed on event:"+activitiEvent.getType()+", handler not found, executionId="+activitiEvent.getExecutionId());
        } else if (!activityEventHandler.getClass().getName().startsWith("com.ymt.bpm.engine")) {
            //check class name
            throw new EngineException(
                    ErrorCode.ATVITIY_FAIL,
                    "Activity getting ActivityEventHandler failed on event:"+activitiEvent.getType()+", handler class is not in correct package, executionId="+activitiEvent.getExecutionId());
        }
        return activityEventHandler;
    }
}
